--
Disable Remote DB Access
Overview
Disabling remote database access means configuring your database and network so the DB service cannot be reached from untrusted networks (especially the public internet). In practice, this is achieved by binding the database listener to local or private interfaces, enforcing firewall rules, and restricting authentication so only approved application hosts and administrators can connect.
History
- Early self-managed deployments often exposed database ports directly for convenience and remote administration.
- As internet scanning and credential stuffing became common, security practice shifted toward private networking, bastion access, and least-privilege connectivity.
- Cloud and container platforms further reinforced this pattern by promoting private subnets and security-group-based isolation.
Adoption
This control is commonly used in:
- Production web applications where the database is a private dependency
- Regulated environments requiring network segmentation and least privilege
- Cloud deployments using private subnets/VPCs and security groups
- Kubernetes and service-mesh environments where database connectivity is limited to specific namespaces/workloads
Maintainer
Maintained by the operating system, database vendors, and infrastructure/community best practices (no single project owns this control).
Best when to use
- The database does not need direct access from the public internet
- Only one or a few application services should connect to the database
- Administrators can reach the database through a bastion host, VPN, or SSH tunnel
- You want to reduce attack surface and prevent opportunistic scanning attacks
Not suitable when
- You truly require direct client-to-database connections from many unmanaged networks (rare for production)
- You cannot provide a secure alternative path for administration (VPN, bastion, private peering)
- Your application architecture depends on remote DB access without a network boundary (re-architect first)
Compatibility notes
- Database configuration differs by engine (MySQL/MariaDB, PostgreSQL, MongoDB, etc.).
- Firewalls differ by OS and distro (
ufw,firewalld, rawiptables/nftables). - Cloud providers add additional network layers (security groups, NACLs) that should be used in addition to host firewalls.
- Containers and orchestrators may require different steps (pod networking, sidecars, ingress controllers).
Restricting DB access can break production traffic if application hosts are not allowlisted correctly. Always validate the current listeners and active clients before applying changes, and keep a rollback path.
How it works
Remote access is controlled at three layers:
-
Database listener binding Which IP addresses and interfaces the DB server listens on (localhost only, private subnet, or all interfaces).
-
Network filtering Firewalls and cloud network controls limit which sources can reach the DB port.
-
Database authentication and host-based access control DB-level rules restrict which users/roles can connect from which hosts and networks.
Pre-change checks (read-only)
Identify DB process and listening ports
On Linux, list listeners and the owning process:
sudo ss -lntp
Filter for common DB ports:
sudo ss -lntp | grep -E ':(3306|5432|27017|6379)\b' || true
Check which interfaces the service is bound to:
127.0.0.1or::1: local-only (not remotely accessible)- Private IP (for example,
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16): private network only 0.0.0.0or::: all interfaces (remote access possible)
Confirm firewall status
Depending on your distro:
sudo ufw status verbose 2>/dev/null || true
sudo firewall-cmd --state 2>/dev/null || true
sudo nft list ruleset 2>/dev/null | head || true
Confirm cloud/network controls (if applicable)
Use your cloud provider console/CLI to confirm:
- Security group rules
- Network ACL rules
- Subnet routing (public vs private)
- Load balancer rules (DB ports should not be exposed)
Recommended target state
| Component | Target | Notes |
|---|---|---|
| -- | - | - |
| DB bind address | localhost or private interface only | Prefer private subnet if apps are remote |
| Firewall | Allow DB port only from trusted sources | Deny everything else by default |
| DB authentication | Least privilege accounts, host restrictions | Do not rely on network-only controls |
| Admin access | VPN/bastion/SSH tunnel | Avoid direct public access |
Database-specific configuration
MySQL / MariaDB
Bind to localhost (local-only)
-
Locate config (common paths vary):
/etc/mysql/mysql.conf.d/mysqld.cnf/etc/my.cnf/etc/my.cnf.d/*.cnf
-
Set bind address:
[mysqld]
bind-address = 127.0.0.1
- Validate config (read-only) if available:
mysqld --help --verbose 2>/dev/null | head -n 50 || true
- Restart service:
sudo systemctl restart mysql 2>/dev/null || sudo systemctl restart mariadb
- Verify listener:
sudo ss -lntp | grep -E ':(3306)\b' || true
Restrict remote users
Even if bound to private IPs, ensure users are not broadly allowed:
-- Example pattern: prefer host-specific accounts
-- SHOW users and allowed hosts:
SELECT user, host FROM mysql.user;
MySQL accounts include a host field. Avoid wildcard hosts (%) for privileged users where possible, and prefer application subnets or specific hosts.
PostgreSQL
PostgreSQL controls remote access via both listening addresses and pg_hba.conf.
Bind to localhost (local-only)
- Find active config file paths:
psql -U postgres -c "SHOW config_file;" 2>/dev/null || true
psql -U postgres -c "SHOW hba_file;" 2>/dev/null || true
- Set
listen_addresses:
listen_addresses = 'localhost'
- Reload/restart:
sudo systemctl reload postgresql 2>/dev/null || sudo systemctl restart postgresql
- Verify listener:
sudo ss -lntp | grep -E ':(5432)\b' || true
Tighten pg_hba.conf
Use CIDR-restricted rules and avoid broad trust methods.
Example restrictive entries:
# Local connections
local all all peer
# App subnet only (example)
host appdb appuser 10.0.10.0/24 scram-sha-256
Avoid trust for non-local connections. Prefer scram-sha-256 on modern PostgreSQL deployments.
MongoDB
MongoDB controls remote access primarily via bindIp (and auth settings).
Bind to localhost (local-only)
In mongod.conf:
net:
bindIp: 127.0.0.1
port: 27017
Restart and verify:
sudo systemctl restart mongod
sudo ss -lntp | grep -E ':(27017)\b' || true
If MongoDB is currently serving remote application traffic, binding to localhost will immediately break those clients. Confirm application topology first.
Redis (commonly misconfigured)
Redis is frequently exposed accidentally; lock it down aggressively.
Bind to localhost or private interface
In redis.conf:
bind 127.0.0.1 ::1
protected-mode yes
Restart and verify:
sudo systemctl restart redis 2>/dev/null || sudo systemctl restart redis-server
sudo ss -lntp | grep -E ':(6379)\b' || true
Never expose Redis directly to the public internet. If remote access is required, use private networking and strong authentication controls, and prefer TLS-enabled deployments where supported.
Firewall controls (host-level)
Firewall configuration is OS- and distro-specific. The goal is:
- Deny DB ports from untrusted networks
- Allow DB ports only from application hosts or private subnets
- Keep SSH access intact
UFW (Ubuntu/Debian)
Check status:
sudo ufw status verbose
Allow DB access only from an app subnet (example for PostgreSQL):
sudo ufw allow from 10.0.10.0/24 to any port 5432 proto tcp
sudo ufw deny 5432/tcp
Apply/enable if not enabled:
sudo ufw enable
If you manage the host over SSH, confirm an explicit allow rule for SSH (port 22 or your custom port) before enabling or changing firewall defaults.
firewalld (Fedora/RHEL)
Check status:
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
Allow from a source subnet (example for MySQL):
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.10.0/24" port protocol="tcp" port="3306" accept'
sudo firewall-cmd --permanent --remove-service=mysql 2>/dev/null || true
sudo firewall-cmd --reload
Secure alternatives to remote DB access
SSH tunneling (admin access)
From your workstation:
ssh -L 5432:127.0.0.1:5432 user@db-host
Then connect locally:
psql -h 127.0.0.1 -p 5432 -U appuser appdb
SSH tunneling reduces attack surface because the DB remains local/private, while admins can still reach it through authenticated, logged access.
VPN / bastion host
For production environments, prefer:
- VPN access into a private subnet, or
- A bastion host with strict access controls and auditing
Validation checklist
Confirm ports are not exposed
On the DB host:
sudo ss -lntp | grep -E ':(3306|5432|27017|6379)\b' || true
From a machine that should not have access, verify the port is blocked (example):
nc -vz db-host 5432
Confirm application connectivity
From an allowed application host, test DB connectivity using the application’s configured credentials and endpoint.
Troubleshooting
Application suddenly cannot connect
Common causes:
- DB now bound to localhost but app is remote
- Firewall denies app subnet or host
- DB-level rules deny the client network
- DNS points to a public IP while DB is private-only
Safe checks:
sudo ss -lntp | grep -E ':(3306|5432|27017|6379)\b' || true
sudo tail -n 100 /var/log/syslog 2>/dev/null || true
sudo journalctl -u mysql -n 100 2>/dev/null || true
sudo journalctl -u postgresql -n 100 2>/dev/null || true
DB still reachable remotely after changes
Common causes:
- Cloud security group still allows the port
- The service is listening on
0.0.0.0or a public interface - A load balancer or port-forward is exposing the DB
Verify listener bindings:
sudo ss -lntp | grep -E ':(3306|5432|27017|6379)\b' || true
Quick reference
| Goal | Read-only check | Typical control |
|---|---|---|
| -- | - | -- |
| See listening DB ports | sudo ss -lntp | Bind to localhost/private IP |
| Confirm firewall status | ufw status / firewall-cmd --state | Deny DB ports publicly |
| MySQL local-only | sudo ss -lntp | grep 3306 | bind-address = 127.0.0.1 |
| PostgreSQL local-only | sudo ss -lntp | grep 5432 | listen_addresses = 'localhost' |
| MongoDB local-only | sudo ss -lntp | grep 27017 | bindIp: 127.0.0.1 |
| Redis local-only | sudo ss -lntp | grep 6379 | bind 127.0.0.1 ::1 |
| Safe admin access | N/A | SSH tunnel, VPN, bastion |